package com.sky.utils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.sky.properties.WeChatProperties;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import org.apache.commons.lang.RandomStringUtils;
import org.apache.http.HttpHeaders;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.math.BigDecimal;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
import java.util.List;

/**
 * 微信支付工具类
 */
@Component
public class WeChatPayUtil {
	
	//微信支付下单接口地址
	public static final String JSAPI = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi";
	
	//申请退款接口地址
	public static final String REFUNDS = "https://api.mch.weixin.qq.com/v3/refund/domestic/refunds";
	
	@Autowired
	private WeChatProperties weChatProperties;
	
	/**
	 * 获取调用微信接口的客户端工具对象
	 *
	 * @return
	 */
	private CloseableHttpClient getClient() {
		PrivateKey merchantPrivateKey = null;
		try {
			//merchantPrivateKey商户API私钥，如何加载商户API私钥请看常见问题
			merchantPrivateKey = PemUtil.loadPrivateKey(new FileInputStream(new File(weChatProperties.getPrivateKeyFilePath())));
			//加载平台证书文件
			X509Certificate x509Certificate =
					PemUtil.loadCertificate(new FileInputStream(new File(weChatProperties.getWeChatPayCertFilePath())));
			//wechatPayCertificates微信支付平台证书列表。你也可以使用后面章节提到的“定时更新平台证书功能”，而不需要关心平台证书的来龙去脉
			List<X509Certificate> wechatPayCertificates = Arrays.asList(x509Certificate);
			
			WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
					.withMerchant(weChatProperties.getMchid(), weChatProperties.getMchSerialNo(), merchantPrivateKey)
					.withWechatPay(wechatPayCertificates);
			
			// 通过WechatPayHttpClientBuilder构造的HttpClient，会自动的处理签名和验签
			CloseableHttpClient httpClient = builder.build();
			return httpClient;
		} catch (FileNotFoundException e) {
			e.printStackTrace();
			return null;
		}
	}
	
	/**
	 * 发送post方式请求
	 *
	 * @param url
	 * @param body
	 * @return
	 */
	private String post(String url, String body) throws Exception {
		CloseableHttpClient httpClient = getClient();
		
		HttpPost httpPost = new HttpPost(url);
		httpPost.addHeader(HttpHeaders.ACCEPT, ContentType.APPLICATION_JSON.toString());
		httpPost.addHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString());
		httpPost.addHeader("Wechatpay-Serial", weChatProperties.getMchSerialNo());
		httpPost.setEntity(new StringEntity(body, "UTF-8"));
		
		CloseableHttpResponse response = httpClient.execute(httpPost);
		try {
			String bodyAsString = EntityUtils.toString(response.getEntity());
			return bodyAsString;
		} finally {
			httpClient.close();
			response.close();
		}
	}
	
	/**
	 * 发送get方式请求
	 *
	 * @param url
	 * @return
	 */
	private String get(String url) throws Exception {
		CloseableHttpClient httpClient = getClient();
		
		HttpGet httpGet = new HttpGet(url);
		httpGet.addHeader(HttpHeaders.ACCEPT, ContentType.APPLICATION_JSON.toString());
		httpGet.addHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString());
		httpGet.addHeader("Wechatpay-Serial", weChatProperties.getMchSerialNo());
		
		CloseableHttpResponse response = httpClient.execute(httpGet);
		try {
			String bodyAsString = EntityUtils.toString(response.getEntity());
			return bodyAsString;
		} finally {
			httpClient.close();
			response.close();
		}
	}
	
	/**
	 * jsapi下单
	 *
	 * @param orderNum    商户订单号
	 * @param total       总金额
	 * @param description 商品描述
	 * @param openid      微信用户的openid
	 * @return
	 */
	private String jsapi(String orderNum, BigDecimal total, String description, String openid) throws Exception {
		JSONObject jsonObject = new JSONObject();
		jsonObject.put("appid", weChatProperties.getAppid());
		jsonObject.put("mchid", weChatProperties.getMchid());
		jsonObject.put("description", description);
		jsonObject.put("out_trade_no", orderNum);
		jsonObject.put("notify_url", weChatProperties.getNotifyUrl());
		
		JSONObject amount = new JSONObject();
		amount.put("total", total.multiply(new BigDecimal(100)).setScale(2, BigDecimal.ROUND_HALF_UP).intValue());
		amount.put("currency", "CNY");
		
		jsonObject.put("amount", amount);
		
		JSONObject payer = new JSONObject();
		payer.put("openid", openid);
		
		jsonObject.put("payer", payer);
		
		String body = jsonObject.toJSONString();
		return post(JSAPI, body);
	}
	
	/**
	 * 小程序支付
	 *
	 * @param orderNum    商户订单号
	 * @param total       金额，单位 元
	 * @param description 商品描述
	 * @param openid      微信用户的openid
	 * @return
	 */
	public JSONObject pay(String orderNum, BigDecimal total, String description, String openid) throws Exception {
		//统一下单，生成预支付交易单
		String bodyAsString = jsapi(orderNum, total, description, openid);
		//解析返回结果
		JSONObject jsonObject = JSON.parseObject(bodyAsString);
		System.out.println(jsonObject);
		
		String prepayId = jsonObject.getString("prepay_id");
		if (prepayId != null) {
			String timeStamp = String.valueOf(System.currentTimeMillis() / 1000);
			String nonceStr = RandomStringUtils.randomNumeric(32);
			ArrayList<Object> list = new ArrayList<>();
			list.add(weChatProperties.getAppid());
			list.add(timeStamp);
			list.add(nonceStr);
			list.add("prepay_id=" + prepayId);
			//二次签名，调起支付需要重新签名
			StringBuilder stringBuilder = new StringBuilder();
			for (Object o : list) {
				stringBuilder.append(o).append("\n");
			}
			String signMessage = stringBuilder.toString();
			byte[] message = signMessage.getBytes();
			
			Signature signature = Signature.getInstance("SHA256withRSA");
			signature.initSign(PemUtil.loadPrivateKey(new FileInputStream(new File(weChatProperties.getPrivateKeyFilePath()))));
			signature.update(message);
			String packageSign = Base64.getEncoder().encodeToString(signature.sign());
			
			//构造数据给微信小程序，用于调起微信支付
			JSONObject jo = new JSONObject();
			jo.put("timeStamp", timeStamp);
			jo.put("nonceStr", nonceStr);
			jo.put("package", "prepay_id=" + prepayId);
			jo.put("signType", "RSA");
			jo.put("paySign", packageSign);
			
			return jo;
		}
		return jsonObject;
	}
	
	/**
	 * 申请退款
	 *
	 * @param outTradeNo  商户订单号
	 * @param outRefundNo 商户退款单号
	 * @param refund      退款金额
	 * @param total       原订单金额
	 * @return
	 */
	public String refund(String outTradeNo, String outRefundNo, BigDecimal refund, BigDecimal total) throws Exception {
		JSONObject jsonObject = new JSONObject();
		jsonObject.put("out_trade_no", outTradeNo);
		jsonObject.put("out_refund_no", outRefundNo);
		
		JSONObject amount = new JSONObject();
		amount.put("refund", refund.multiply(new BigDecimal(100)).setScale(2, BigDecimal.ROUND_HALF_UP).intValue());
		amount.put("total", total.multiply(new BigDecimal(100)).setScale(2, BigDecimal.ROUND_HALF_UP).intValue());
		amount.put("currency", "CNY");
		
		jsonObject.put("amount", amount);
		jsonObject.put("notify_url", weChatProperties.getRefundNotifyUrl());
		
		String body = jsonObject.toJSONString();
		
		//调用申请退款接口
		return post(REFUNDS, body);
	}
}
